const path = require('path');
const compressing = require('compressing');
const moment = require('moment');
const { getCountableKinds } = require('../../../file-elements');
const { resultKind } = require('../../../tests');
const BaseReporter = require('../base-report');
const classCreator = require('./../base-report-test-case-transformer');
const testModels = require('./../../../tests');
const functionModels = require('./../../../functions');
const utility = require('../../../utility');
const logger = require('../../../logger');
const ProgressIndicator = require('../../../progress-indicator');
const { testType } = testModels;
const typeNameDic = {
  [testType.gn]: '功能',
  [testType.jk]: '接口',
  [testType.bj]: '边界',
  [testType.lj]: '逻辑',
  [testType.xn]: '性能',
};
class Report extends BaseReporter {
  constructor(versionId, locale, version = null, project = null, creator = null, outputDir = null, format = null, complete = false) {
    super(versionId, locale, version, project, creator, outputDir, null, format);
    this.baseDir = __dirname;
    this.complete = complete;
    this.ReportTestCaseTransformer = classCreator('unitTest');
    // this.functionList = [];
    // this.fileCoverageList = [];
    this.testCaseList = [];
    this.reportTime = new Date();
  }

  /**
   * 报告生成
   */
  async generate(progressIndicator = new ProgressIndicator()) {
    const [progress1, progress2] = progressIndicator.split([0.7, 0.3]);
    const result = {
      cachePath: this.outputDir,
    };
    await this.spliteByFile(null, progress1);
    await this.generateTestCaseReport();
    progress2.advance(0.3);
    await this.generateTestCaseDesc();
    progress2.advance(0.3);
    const fileName = `${this.project.projectName}-${this.version.versionName}.${this.getReportName()}.${this.format}.zip`;
    result.reportPath = path.join(this.outputDir, fileName);
    const reportsDir = path.join(this.outputDir, this.project.projectName);
    await compressing.zip.compressDir(reportsDir, result.reportPath);
    progress2.finish();
    return result;
  }

  async generateTestCaseReport() {
    const template = 'testcase-report';
    const output = path.join(
      this.outputDir,
      this.project.projectName,
      this.version.versionName,
      `XXX-00-0RCB_A_软件${this.isIntegrationTest() ? '集成' : '单元'}测试报告`
    );
    let issueNumber = 0;
    this.testCaseList.forEach((tc) => {
      if (tc.resultKind === resultKind.failed) {
        tc.issueNumber = `XXX-00-0RCB-WT-${`${issueNumber += 1}`.padStart(2, 0)}`;
      } else {
        tc.issueNumber = '无';
      }
    });
    return this.generateReport(template, {
      reportTime: moment(this.reportTime).format('YYYY年M月'),
      reportTimeNumber: moment(this.reportTime).format('YYYYMMDD'),
      testCaseList: this.testCaseList,
    }, output);
  }

  async generateTestCaseDesc() {
    const template = 'testcase';
    const output = path.join(
      this.outputDir,
      this.project.projectName,
      this.version.versionName,
      `XXX-00-0RCM_A_软件${this.isIntegrationTest() ? '集成' : '单元'}测试说明`
    );
    return this.generateReport(template, {
      reportTime: moment(this.reportTime).format('YYYY年M月'),
      reportTimeNumber: moment(this.reportTime).format('YYYYMMDD'),
      testCaseList: this.testCaseList,
    }, output);
  }

  async handleFile(file, versionDir) {
    const result = await super.handleFile(file, versionDir);
    const { functions: functionAll, file: f, typeSystem } = result;
    const functions = functionAll.filter((func) => getCountableKinds().find((kind) => kind === func.kind));
    utility.arrayChunkOperation(functions, 5, async (func) => {
      const { tests } = func;
      const funcVariables = functionModels.getFunctionVariableJson(func.variables);
      const testcasesDetails = await this.getTestDetailsOfFunction(typeSystem, func, funcVariables, tests, f);
      this.testCaseList.push(...testcasesDetails);
    });
  }

  /**
   * 获取测试用例详情
   * @param {Object} typeSystem 文件类型系统
   * @param {Object} func  函数
   * @param {Object} funcVariables 函数variable
   * @param {Array[Object]} testcases 测试用例
   */
  async getTestDetailsOfFunction(typeSystem, func, funcVariables, testcases, file) {
    const testcasesDetails = [];
    testcases
      .forEach((testcase, index) => {
        const newTestcase = testModels.getJson(testcase, this.i18n);
        const ReportTestCaseTransformer = classCreator(this.isIntegrationTest() && 'integrationTest');
        const transformer = new ReportTestCaseTransformer(
          newTestcase,
          funcVariables,
          typeSystem,
          this.i18n
        );
        try {
          transformer.transform(this.complete);
        } catch (error) {
          logger.error(`testCase transform error: fileId: ${func.fileId} function name: ${func.functionName} test case number: ${testcase.number}:`, error);
        }
        if (utility.isArrayType(transformer.input)) {
          transformer.input.forEach((input) => {
            // eslint-disable-next-line no-control-regex
            input.variable = input.variable.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
            // 处理变量值显示格式
            if (input.value) {
              input.value = ` = ${input.value}`;
            }
          });
        }
        if (utility.isArrayType(transformer.output)) {
          transformer.output.forEach((output) => {
            // eslint-disable-next-line no-control-regex
            output.variable = output.variable.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
            // 处理变量值显示格式
            if (output.result) {
              output.result = ` = ${output.result}`;
            }
            if (output.value) {
              output.value = ` = ${output.value}`;
            }
          });
        }
        const newInputDetails = [];
        // const newOutputDetails = [];
        const newInputStubDetails = [];

        let currentCategory = '';
        transformer.input.forEach((vobj) => {
          if (vobj.category) {
            currentCategory = vobj.category;
          }
          switch (currentCategory) {
            case '桩函数':
              newInputStubDetails.push(vobj);
              break;
            default:
              if (vobj.category && vobj.variable) {
                newInputDetails.push({
                  category: `${vobj.category}:`,
                  value: '',
                  variable: '',
                });
                newInputDetails.push({
                  category: '',
                  value: vobj.value,
                  variable: vobj.variable,
                });
              } else {
                newInputDetails.push(vobj);
              }
              break;
          }
        });
        // eslint-disable-next-line prefer-const
        let [expected, results] = transformer.output.reduce(([expected, results], vobj) => {
          if (vobj.category) {
            currentCategory = vobj.category;
          }
          if (vobj.value) {
            expected[currentCategory] = expected[currentCategory] || [];
            expected[currentCategory].push({
              category: '',
              value: vobj.value,
              variable: vobj.variable,
            });
          }
          if (vobj.result) {
            results[currentCategory] = results[currentCategory] || [];
            results[currentCategory].push({
              category: '',
              result: vobj.result,
              variable: vobj.variable,
            });
          }
          return [expected, results];
        }, [{}, {}]);
        let passed;
        let excuted;
        switch (testcase.resultKind) {
          case resultKind.succeed:
          case resultKind.noResult:
          case resultKind.nonStrictSucceed:
            passed = true;
            excuted = true;
            break;
          case resultKind.failed:
            passed = false;
            excuted = true;
            break;
          default:
            excuted = false;
            break;
        }
        const checked = '■';
        const unChecked = '□';
        const item = {
          index: `${func.functionName}_${`${index + 1}`.padStart(4, '0')}`,
          testId: testcase._id,
          resultKind: testcase.resultKind,
          details: {
            prelude: transformer.prelude || [{ line: '无' }],
            input: newInputDetails,
            inputStubs: newInputStubDetails.length === 0 ? [{ category: '', variable: '', value: '无' }] : newInputStubDetails,
            output: {
              results: Object.keys(results).reduce((arr, category) => {
                const categoryHeader = { category, variable: '', result: '' };
                arr.push(...[categoryHeader, ...results[category]]);
                return arr;
              }, []),
              expected: Object.keys(expected).reduce((arr, category) => {
                const categoryHeader = { category, variable: '', value: '' };
                arr.push(...[categoryHeader, ...expected[category]]);
                return arr;
              }, []),
            },
          },
          identifier: testcase.identifier,
          functionId: func._id,
          filePath: file.path,
          fileName: file.fileName,
          functionName: func.functionName,
          requirementInfo: testcase.requirementInfo || `《xxx软件设计说明》${func.functionName}`,
          description: testcase.description,
          typeName: `${typeNameDic[testcase.typeName]}测试`,
          excutedIcon: excuted ? checked : unChecked,
          nonExcutedIcon: !excuted ? checked : unChecked,
          passedIcon: excuted && passed ? checked : unChecked,
          nonPassedIcon: excuted && (!passed) ? checked : unChecked,
        };
        if (testcase.resultKind === resultKind.noResult || testcase.resultKind === resultKind.nonStrictSucceed) {
          // 实际值赋给预期值
          item.details.output.expected = item.details.output.results.map((item) => ({
            category: item.category,
            value: item.result,
            variable: item.variable,
          }));
        }
        testcasesDetails.push(item);
      });
    return testcasesDetails;
  }
}

module.exports = Report;
